import { Store, Authority, Role, User, Level } from '../interface'
import { join } from 'path'
import { readFileSync, writeFileSync, watchFile } from 'fs';
import { generateID, MD5 } from '../utils'

const default_pathname = join(process.cwd(), '.authority.json')
const bak_pathname = join(__dirname, '../../.authority.json')

export class FileSystemStore implements Store {
    createToken (acc: { name: any; password: string; }) {
        return MD5(`${acc.name}:${MD5(acc.password)}`)
    }

    __obj: {
        authorities: Authority[]
        roles: Role[]
        users: User[]
    }
    usePasswordChange = true
    __save: () => void
    __userMap: Map<string, User>
    __initUserMap = () => {
        this.__userMap = new Map()
        this.__obj.users.map(user => {
            const key = MD5(`${user.name}:${user.password}`)
            this.__userMap.set(key, {
                ...user,
                token: key,
                role: this.__obj.roles.find(r => r.id === user.roleId)
            })
        })
    }
    constructor(pathname = default_pathname) {
        this.__save = () => {
            const str = JSON.stringify(this.__obj, null, 2)
            writeFileSync(pathname, str)
            writeFileSync(bak_pathname, str)
        }
        this.__obj = JSON.parse(readFileSync(pathname).toString())
        this.__initUserMap()
        watchFile(pathname, () => {
            this.__obj = JSON.parse(readFileSync(pathname).toString())
            this.__initUserMap()
        })        
    }
    authorities = () => this.__obj.authorities
    roles = () => this.__obj.roles
    users = () => this.__obj.users

    addRole = (name: string) => {
        const id = generateID(this.__obj.roles)
        this.__obj.roles.push({
            id, name, authorities: {}
        })
        this.__save()
        return id
    }
    delRole = (roleId: string) => {
        let id = Number(roleId) || 0
        this.__obj.roles = this.__obj.roles.filter(r => r.id !== id)
        this.__obj.users.map(u => {
            if (u.roleId === id) {
                delete u.roleId
            }
        })
        this.__save()
    }
    addUser = (name: string, password: string, nickname?: string) => {
        const id = generateID(this.__obj.users)
        this.__obj.users.push({ id, name, nickname, password: MD5(password || name)})
        this.__save()
        return id
    }
    delUser = (userId: string) => {
        this.__obj.users = this.__obj.users.filter(u => u.id !== Number(userId))
        this.__save()
    }
    changeUserPassword = (userId: string, password?: string) => {
        this.__obj.users.find(user => {
            if (user.id === Number(userId) && password) {
                user.password = MD5(password)
                return true
            }
        })
        this.__save()
    }
    updateRoleAuthority = (roleId: string, authorityId: string, level: Level) => {
        this.__obj.roles.find(role => {
            if (role.id === Number(roleId)) {
                role.authorities = role.authorities || {}
                role.authorities[authorityId] = level
                return true
            }
        })
        this.__save()
    }
    updateUserRole = (userId: string, roleId: string) => {
        this.__obj.users.find(user => {
            if (user.id === Number(userId)) {
                user.roleId = Number(roleId)
                return true
            }
        })
        this.__save()
    }

    updateUserLockIPs = (userId: string, ips: string[]) => {
        this.__obj.users.find(user => {
            if (user.id === Number(userId)) {
                user.lock_ips = ips
                return true
            }
        })
        this.__save()
    }
    updateUserExpires = (userId: string, expires: number) => {
        this.__obj.users.find(user => {
            if (user.id === Number(userId)) {
                user.expires = expires
                return true
            }
        })
        this.__save()
    }

    refresh () {}
    getLoginUser = (key: string) => {
        return this.__userMap.get(key)
    }
    lookLoginUser = async (key: string) => {
        return this.__userMap.get(key)
    }
}